﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NSDecisionTree.Graphic
{
    class Node
    {
        string name;
        Node parent;
        Edge edgeToParent;
        List<Edge> edgesToChilds;

        internal List<Edge> EdgesToChilds
        {
            get { return edgesToChilds; }
            set { edgesToChilds = value; }
        }
        List<Node> children;

        internal List<Node> Children
        {
            get { return children; }
            set { children = value; }
        }
        bool isLeaf = false;

        public bool IsThisNodeLeaf
        {
            get { return isLeaf; }
        }
        int level;
        int item;
        int sumChildCount;
        double entropy;
        int xPos = 0;
        int yPos = 0;

        public int XPos
        {
            get { return xPos; }
            set { xPos = value; }
        }

        public int YPos
        {
            get { return yPos; }
            set { yPos = value; }
        }

        public int SumChildCount
        {
            get { return sumChildCount; }
            set 
            { 
                sumChildCount = SumChildCount + value;
                if (parent != null)
                    parent.SumChildCount = value;
            }
        }

        public int Level
        {
            get { return level; }
            set { level = value; }
        }

        internal Node Parent
        {
            get { return parent; }
            set { parent = value; }
        }

        internal Edge EdgeToParent
        {
            get { return edgeToParent; }
            set { edgeToParent = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public Node(String Name, double Entropy)
        {
            name = Name;
            children = new List<Node>();
            edgesToChilds = new List<Edge>();
            parent = null;
            edgeToParent = null;
            isLeaf = true;
            level = 0;
            item = 0;
            sumChildCount = 0;
            entropy = Entropy;
        }

        public void AddChildNode(Node child, Edge edgeToChild, List<int> numChild)
        {
            child.Level = level + 1;
            child.Parent = this;
            child.EdgeToParent = edgeToChild;
            edgesToChilds.Add(edgeToChild);
            children.Add(child);
            isLeaf = false;
            child.item = numChild[level + 1] - 1;
        }

        public void SetXYPos(int Level, int Item, int XPos, int YPos)
        {
            if (Level == 0)
            {
                xPos = XPos;
                yPos = YPos;
            }
            else
            {
                SetXYPosChild(this, Level, Item, XPos, YPos);
            }
        }

        private bool SetXYPosChild(Node currentRootNode, int Level, int Item, int XPos, int YPos)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item)
                {
                    child.xPos = XPos;
                    child.yPos = YPos;
                    return true;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (SetXYPosChild(child, Level, Item, XPos, YPos))
                    return true;
            }
            return false;
        }

        public Node getNodeInRange(int x, int y, int xRange, int yRange,float sizeMultiplicator)
        {
            int currentX = (int)(x / sizeMultiplicator);
            int currentY = (int)(y / sizeMultiplicator);
            int startX = xPos;
            int startY = yPos;
            int endX = startX + xRange;
            int endY = startY + yRange;

            if (currentX >= startX && currentX <= endX && currentY >= startY && currentY <= endY)
            {
                return this;
            }
            else
            {
                return searchNodeInRange(this, x, y, xRange, yRange, sizeMultiplicator);
            }
        }

        private Node searchNodeInRange(Node currentRootNode, int x, int y, int xRange, int yRange, float sizeMultiplier)
        {
            foreach (Node child in currentRootNode.children)
            {
                int currentX = (int)(x / sizeMultiplier);
                int currentY = (int)(y / sizeMultiplier);
                int startX = child.xPos;
                int startY = child.yPos;
                int endX = startX + xRange;
                int endY = startY + yRange;

                if (currentX >= startX && currentX <= endX && currentY >= startY && currentY <= endY)
                {
                    return child;
                }
            }

            Node retVal = null;
            foreach (Node child in currentRootNode.children)
            {
                retVal = (searchNodeInRange(child, x, y, xRange, yRange, sizeMultiplier));
                if (retVal != null)
                    break;
            }

            return retVal;
        
        }

        public string GetStringItem(int Level, int Item)
        {
            string retVal = String.Empty;

            if (Level == 0)
                return name;
            else 
            {
                if (searchItem(this,Level, Item,out retVal))
                    return retVal;
            }
            return "Error";
            
        }

        public string GetAdditionalInfo(int Level, int Item)
        {
            string retVal = String.Empty;

            if (Level == 0)
            {
                retVal = children.Count.ToString() + " [";
                for(int i = 0; i < children.Count; i++)
                {
                    retVal += children[i].sumChildCount.ToString();
                    if ((i + 1) < children.Count)
                        retVal += ",";
                }
                retVal += "] " + entropy.ToString("0.00000");
                return  retVal;
            }
            else
            {
                if (searchAdditionalInfo(this, Level, Item, out retVal))
                    return retVal;
            }
            return "Error";
        }

        public int getChildrenCount(int Level, int Item)
        {
            int retVal = 0;

            if (Level == 0)
                return children.Count;
            else
            {
                if (searchChildrenCount(this, Level, Item, out retVal))
                    return retVal;
            }
            return -1;
        }

        public string getEdgeText(int Level, int Item)
        {
            string retVal = String.Empty;

            if (Level == 0)
                return String.Empty;
            else
            {
                if (searchEdgeText(this, Level, Item, out retVal))
                    return retVal;
            }
            return String.Empty;
        }

        public List<string> getAllLeafValues()
        {
            List<string> retVal = new List<string>();
            getLeafsList(this, ref retVal);
            return retVal;
        }

        public bool IsLeaf(int Level, int Item)
        {
            if (Level == 0)
                return isLeaf;
            else
            {
                if (searchLeafs(this, Level, Item))
                    return true;
            }
            return false;

        }

        private bool searchItem(Node currentRootNode, int Level, int Item, out string retVal)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item)
                {
                    retVal = child.name;
                    return true;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (searchItem(child, Level, Item, out retVal))
                    return true;
            }
            retVal = String.Empty;
            return false;
        }

        private bool searchAdditionalInfo(Node currentRootNode, int Level, int Item, out string retVal)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item && (! child.isLeaf) )
                {
                    retVal = child.children.Count.ToString() + " [";
                    for (int i = 0; i < child.children.Count; i++)
                    {
                        retVal += child.children[i].sumChildCount.ToString();
                        if ((i + 1) < child.children.Count)
                            retVal += ",";
                    }
                    retVal += "] " + child.entropy.ToString("0.00000");
                    return true;
                }
                else if (child.level == Level && child.item == Item && child.isLeaf)
                {
                    retVal = "Leaf: " + child.SumChildCount;
                    return true;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (searchAdditionalInfo(child, Level, Item, out retVal))
                    return true;
            }
            retVal = String.Empty;
            return false;
        }

        private bool searchChildrenCount(Node currentRootNode, int Level, int Item, out int retVal)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item)
                {
                    retVal = child.children.Count;
                    return true;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (searchChildrenCount(child, Level, Item, out retVal))
                    return true;
            }
            retVal = -1;
            return false;
        }
       
        private bool searchEdgeText(Node currentRootNode, int Level, int Item, out string retVal)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item)
                {
                    retVal = child.edgeToParent.ToString();
                    return true;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (searchEdgeText(child, Level, Item, out retVal))
                    return true;
            }
            retVal = String.Empty;
            return false;
        }

        private bool searchLeafs(Node currentRootNode, int Level, int Item)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.level == Level && child.item == Item)
                {
                    return child.isLeaf;
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                if (searchLeafs(child, Level, Item))
                    return true;
            }
            return false;
        }

        private void getLeafsList(Node currentRootNode, ref List<string> retVal)
        {
            foreach (Node child in currentRootNode.children)
            {
                if (child.isLeaf)
                {
                    if (! retVal.Contains(child.name))
                    {
                        retVal.Add(child.name);
                    }
                }
            }

            foreach (Node child in currentRootNode.children)
            {
                getLeafsList(child, ref retVal);
            }
        }
    }
}
